;*******************************************************************************
; Director - Memory - string, list and memory manipulation
;
; Copyright (C) 2003, Nick Craig-Wood and Philip Ludlam
;
;This program is free software; you can redistribute it and/or modify it under
;the terms of the GNU General Public License as published by the Free Software
;Foundation; either version 2 of the License, or (at your option) any later
;version.
;
;This program is distributed in the hope that it will be useful, but WITHOUT ANY
;WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
;PARTICULAR PURPOSE. See the GNU General Public License for more details.
;
;You should have received a copy of the GNU General Public License along with
;this program; if not, write to the Free Software Foundation, Inc., 59 Temple
;Place - Suite 330, Boston, MA 02111-1307, USA
;
;*******************************************************************************
;----h- Director.s.Memory
; Name
;   Memory
;
; Purpose
;   String, list and memory manipulation
;------
;*******************************************************************************


		TTL	> Memory - string, list and memory manipulation

		GET	OSLib:oslib.hdr.Wimp
		GET	OSLib:oslib.hdr.Territory
		GET	OSLib:oslib.hdr.OSModule
		GET	OSLib:oslib.hdr.OSHeap
		GET	AsmLib2:hdr.RegsBoth
		GET	AsmLib2:hdr.MacrosBoth
		GET	h.WorkSpace
		GET	h.ListMacros
		GET	h.Constants
		GET	h.Menus

		AREA	|Memory|, CODE, READONLY


;*******************************************************************************
;----f- Director.s.Memory.MemoryInitialise
; Name
;   MemoryInitialise
;
; Purpose
;   This initialises the memory manager
;
; Entry
;   none
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     VC
;------
;*******************************************************************************


MemoryInitialise	ROUTINE	"r0-r11", EXPORT

		; Read the page size of the machine

		SWI	XOS_ReadMemMapInfo
		STR	r0, MemoryPageSize
		STR	r1, MemoryPages

		; Try to create a Dynamic area

		MOV	r2, r0				; Initial size of area (1 page)
		MOV	r0, #OSDynamicArea_Create
		MOV	r1, #-1				; Area number
		MOV	r3, #-1				; Area base address
		MOV	r4, #2_10000000			; Flags: not dragable
		MOV	r5, #1024*1024			; Maximum size of area
		MOV	r6, #0				; Handler address
		MOV	r7, #0				; Handler workspace
		ADR	r8, name$l			; Dynamic area name
		SWI	XOS_DynamicArea
		MOVVS	r1, #0
		STR	r1, DynamicAreaNumber		; set dynamic area if created
		MOVVS	r3, #0
		STR	r3, DynamicAreaBase
		BVS	exit$l

		STR	r2, DynamicAreaSize		; size of dynamic area

		; Now we need to create a heap in the area

		MOV	r0, #OSHeap_Initialise
		MOV	r1, r3				; pointer to the start
		MOV	r3, r2				; initial size
		SWI	XOS_Heap
		BVS	error$l

exit$l		ClearV
		EXIT

error$l		EXIT_ERR

name$l		EQUS0A "$Name Menus"


;*******************************************************************************
;----f- Director.s.Memory.MemoryFinalise
; Name
;   MemoryFinalise
;
; Purpose
;   This finalises the memory manager
;
; Entry
;   none
;
; Exit
;   none
;------
;*******************************************************************************


MemoryFinalise	ROUTINE	"r0-r11", EXPORT

		MOV	r0, #OSDynamicArea_Delete
		LDR	r1, DynamicAreaNumber
		TEQ	r1, #0
		SWINE	XOS_DynamicArea			; delete area, ignore errors

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.memset
; Name
;   memset
;
; Purpose
;   Clear a block of memory to r2
;
; Entry
;   r0 = start address
;   r1 = length
;   r2 = byte to set memory to
;
; Exit
;   none
;------
;*******************************************************************************


memset		ROUTINE	"r0-r1", EXPORT

		CMP	r1, #0
		EXIT	LE				; exit if <=0

loop$l		STRB	r2, [r0], #1
		SUBS	r1, r1, #1
		BGT	loop$l

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.memclear
; Name
;   memclear
;
; Purpose
;   Clear a block of memory to 0
;
; Entry
;   r0 = start address
;   r1 = length
;
; Exit
;   none
;------
;*******************************************************************************


memclear	ROUTINE	"r2", EXPORT

		MOV	r2, #0
		BL	memset

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.memcpy
; Name
;   memcpy
;
; Purpose
;   This is a slow block move, for aligned and non-aligned alike
;
; Entry
;   r0 = source start
;   r1 = destination start
;   r2 = length
;
; Exit
;   none
;------
;*******************************************************************************


memcpy		ROUTINE	"r0-r2", EXPORT

		CMP	r2, #0
		EXIT	LE				; exit if <=0

loop$l		LDRB	lr, [r0], #1
		STRB	lr, [r1], #1
		SUBS	r2, r2, #1
		BGT	loop$l

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.ExtendHeap
; Name
;   ExtendHeap
;
; Purpose
;   This extends the heap by one page
;
; Entry
;
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     VC
;------
;*******************************************************************************


ExtendHeap	ROUTINE	"r0-r4"

		LDR	r4, MemoryPageSize		; amount to change

		LDR	r0, DynamicAreaNumber
		MOV	r1, r4
		SWI	XOS_ChangeDynamicArea
		BVS	error$l

		MOV	r0, #OSHeap_Resize
		LDR	r1, DynamicAreaBase
		MOV	r3, r4
		SWI	XOS_Heap
error$l		STRSP	r0, r0, VS			; if VS ensure r0 is returned to the caller

exit$l		EXIT


;*******************************************************************************
;----f- Director.s.Memory.ShrinkHeap
; Name
;   ShrinkHeap
;
; Purpose
;   This shrinks the heap by as many pages as it can.
;   It does NOT return errors.
;
; Entry
;   none
;
; Exit
;   none
;------
;*******************************************************************************


ShrinkHeap	ROUTINE	"r0-r4"

		MOV	r0, #OSHeap_Resize
		LDR	r1, DynamicAreaBase
		MOV	r3, #-16*1024*1024		; shrink the heap a lot
		SWI	XOS_Heap			; r3 is amount the heap shrunk (+ve)

		LDR	lr, MemoryPageSize
		SUB	lr, lr, #1			; mask
		BIC	r4, r3, lr			; round down to multiple of page size (+ve)

		SUBS	r3, r3, r4			; change to make heap back to a page multiple (+ve)
		MOVNE	r0, #OSHeap_Resize
		SWINE	XOS_Heap			; adjust the heap

		RSBS	r1, r4, #0			; change to make to area (-ve in multiple of page size)
		LDRNE	r0, DynamicAreaNumber
		SWINE	XOS_ChangeDynamicArea

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.malloc
; Name
;   malloc
;
; Purpose
;   Allocates the number of bytes in r0 from the RMA
;   Returns an error if no memory left
;
; Entry
;   r0 = size of memory required
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     r0 = pointer to allocation
;     VC
;------
;*******************************************************************************


malloc		ROUTINE	"r1-r3", EXPORT

		MOV	r3, r0				; r3 = size

		LDR	r1, DynamicAreaBase		; r1 = heap base
		TEQ	r1, #0
		BNE	dynamic$l

		MOV	r0, #OSModule_Alloc
		SWI	XOS_Module
		EXIT	VS				; return error
		B	done$l

dynamic$l	MOV	r0, #OSHeap_Alloc
		SWI	XOS_Heap
		BVC	done$l
		LDR	lr, [r0, #0]			; get error number
		TEQ	lr, #Error_HeapAlloc		; did the block fit
		EXIT	NE				; if that was not the error then return it
		BL	ExtendHeap			; extend the heap
		EXIT	VS				; return error
		B	dynamic$l			; and try again

done$l		MOV	r0, r2
		LDR	r1, [r0, #-4]			; size of block from heap
		LDR	lr, MallocTotalSize
		ADD	lr, lr, r1
		STR	lr, MallocTotalSize		; add up increase in memory used
		LDR	lr, MallocBlocks
		ADD	lr, lr, #1
		STR	lr, MallocBlocks		; show one more block

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.free
; Name
;   free
;
; Purpose
;   Frees the allocation pointed to by r0
;
; Entry
;   Entry	r0 = pointer to allocation
;
; Exit
;   none
;------
;*******************************************************************************


free		ROUTINE	"r0-r3", EXPORT

		MOVS	r2, r0
		EXIT	EQ				; don't free a null pointer

		LDR	r1, [r0, #-4]			; size of block from heap
		LDR	lr, MallocTotalSize
		SUB	lr, lr, r1
		STR	lr, MallocTotalSize		; subtract memory used
		LDR	lr, MallocBlocks
		SUB	lr, lr, #1
		STR	lr, MallocBlocks		; show one less

		LDR	r1, DynamicAreaBase		; r1 = heap base
		TEQ	r1, #0
		BNE	dynamic$l

		MOV	r0, #OSModule_Free
		SWI	XOS_Module			; ignore errors
		EXIT	VC
		B	bad_free$l

dynamic$l	MOV	r0, #OSHeap_Free
		SWI	XOS_Heap
		BVS	bad_free2$l
		BL	ShrinkHeap
		EXIT

bad_free2$l	BL	ShrinkHeap

bad_free$l	ADD	r0, r0, #4
		SWI	XOS_Write0			; give some indication of a bad free
		SWI	XOS_WriteI + 7
		SWI	XOS_NewLine
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.realloc
; Name
;   realloc
;
; Purpose
;   Reallocates the space pointed to by r0 to change in size by r1
;   May return an error
;
; Entry
;   r0  allocation or 0
;   r1 = change in size of allocation
;
; Exit
;   r0  new allocation
;------
;*******************************************************************************


realloc		ROUTINE	"r1-r4", EXPORT

		CMP	r0, #0
		BEQ	doalloc$l			; if null pointer do allocation

		LDR	r4, [r0, #-4]			; old size of block from heap

		MOV	r2, r0				; r2 = pointer to block
		MOV	r3, r1				; r3 = required change

		LDR	r1, DynamicAreaBase		; r1 = heap base
		TEQ	r1, #0
		BNE	dynamic$l

		MOV	r0, #OSModule_Realloc
		SWI	XOS_Module
		B	done$l

dynamic$l	MOV	r0, #OSHeap_Realloc
		SWI	XOS_Heap
		BVC	dynamic_done$l
		LDR	lr, [r0, #0]			; get error number
		TEQ	lr, #Error_HeapAlloc		; did the block fit?
		BNE	done$l				; if not return with error
		BL	ExtendHeap
		BVC	dynamic$l

dynamic_done$l	TEQ	r3, #0				; if we were getting rid of bytes (preserve V)
		BLMI	ShrinkHeap			; try to shrink the heap
		ClearV					; clear V which the BL may have affected


done$l		MOVVC	r0, r2				; new allocated block
		LDRVC	r1, [r0, #-4]			; new size of block from heap
		LDRVC	lr, MallocTotalSize
		SUBVC	lr, lr, r4			; -old
		ADDVC	lr, lr, r1			; +new
		STRVC	lr, MallocTotalSize		; memory used

		EXIT					; return with flags

doalloc$l	MOV	r0, r1
		BL	malloc
		EXIT					; return with flags


;*******************************************************************************
;----f- Director.s.Memory.strlen
; Name
;   strlen
;
; Purpose
;   finds the length of a string
;
; Entry
;   r0  string
;
; Exit
;   r0 = length of string (excluding terminating control char)
;------
;*******************************************************************************


strlen		ROUTINE	"r1", EXPORT

		MOV	r1, r0
		MOV	r0, #0				; length of string

loop$l		LDRB	lr, [r1], #1
		CMP	lr, #32
		ADDGE	r0, r0, #1
		BGE	loop$l

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strchr
; Name
;   strchr
;
; Purpose
;   Returns a pointer to the first occrance of <char> in <string>
;   or NULL if not found.
;
; Entry
;   r0  string
;   r1 = char
;
; Exit
;   If found
;     r0  occrance
;   Otherwise
;     r0 = NULL
;------
;*******************************************************************************


strchr		ROUTINE	"", EXPORT

loop$l		LDRB	lr, [r0]
		CMP	lr, r1
		BEQ	exit$l
		CMP	lr, #32
		ADDGE	r0, r0, #1
		BGE	loop$l
		MOV	r0, #0			; Not found

exit$l		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strlen1
; Name
;   strlen1
;
; Purpose
;   finds the length of a string
;
; Entry
;   r0  string
;
; Exit
;   r0 = length of string (including terminating control char)
;------
;*******************************************************************************


strlen1		ROUTINE	"", EXPORT


		BL	strlen
		ADD	r0, r0, #1

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strlent
; Name
;   strlent
;
; Purpose
;   Finds the length of the string pointed to by r0
;   excluding the supplied terminating char
;
; Entry
;   r0  string
;   r5 = terminating character
;
; Exit
;   r0 = length of string
;------
;*******************************************************************************


strlent		ROUTINE	"r1", EXPORT

		MOV	r1, r0
		MOV	r0, #0				; length of string

loop$l		LDRB	lr, [r1], #1
		CMP	lr, r5
		BEQ	exit$l
		CMP	lr, #31
		BGT	exit$l
		ADD	r0, r0, #1
		B	loop$l

exit$l		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strlent0
; Name
;   strlent0
;
; Purpose
;   Finds the length of the string pointed to by r0
;   and replaces the supplied terminating char with NULL
;
; Entry
;   r0  string
;   r5 = terminating character
;
; Exit
;   r0 = length of string
;------
;*******************************************************************************


strlent0	ROUTINE	"r1", EXPORT

		MOV	r1, r0
		BL	strlent
		ADD	r0, r0, #1
		MOV	lr, #0
		STRB	lr, [r1, r0]

		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strcpy
; Name
;   strcpy
;
; Purpose
;   This moves characters up to a terminator, or StringSize chars
;   (StringSize - 1 characters + a null) from [r0] to [r1]
;   The string is guaranteed 0 terminated even if too long
;   It updates r0 and r1 to point to after their terminating nulls
;
; Entry
;   r0  from string
;   r1  to string
;
; Exit
;   r0  past terminator on from string
;   r1  past terminator on to string
;------
;*******************************************************************************


strcpy		ROUTINE	"r2-r3", EXPORT

		LDR	r2, =StringSize - 1		; max bytes to transfer

loop$l		LDRB	r3, [r0], #1
		STRB	r3, [r1], #1			; copy a byte
		CMP	r3, #32
		BLT	end$l				; return if it was a control character
		SUBS	r2, r2, #1
		BGT	loop$l				; continue if still less than max to do
		ADD	r1, r1, #1

end$l		MOV	r3, #0
		STRB	r3, [r1, #-1]			; zero terminate
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strcpyt
; Name
;   strcpyt
;
; Purpose
;   This moves characters up to a specified terminator, or StringSize chars
;   (StringSize - 1 characters + a null) or it reaches an ASCII character <32
;   from [r0] to [r1]
;   The string is guaranteed 0 terminated even if too long
;   It only updates r0 to point to after their terminating nulls
;
; Entry
;   r0  from string
;   r1  to string
;   r2 = terminator
;
; Exit
;   r0  past terminator on from string
;   r1 is preserved!
;   r2 = terminator
;------
;*******************************************************************************


strcpyt		ROUTINE	"r1, r3-r4", EXPORT

		LDR	r3, = StringSize -1		; max bytes to transfer
loop$l
		LDRB	r4, [r0], #1
		STRB	r4, [r1], #1	   		; copy a byte
		CMP	r4, r2
		BEQ	end$l		   		; return if it was the terminator
		CMP	r4, #32
		BLT	end$l		   		; return if it was a control character
		SUBS	r3, r3, #1
		BGT	loop$l		   		; continue if still less than max to do
		ADD	r1, r1, #1

end$l		MOV	r4, #0
		STRB	r4, [r1, #-1]	   		; zero terminate
		EXIT


;*******************************************************************************
;----f- Director.s.strcpyl
; Name
;   strcpyl
;
; Purpose
;   This moves characters up to r2 chars (where the size in r2
;   includes a terminator) from [r0] to [r1]
;   It updates r0 and r1 to point to after their terminating nulls
;   This routine is commented out in the source code
;
; Entry
;   r0  from string (terminated)
;   r1  to string
;   r2  no. of chars to transfer (it must be 1 or greater)
;
; Exit
;   r0  past terminator on from string
;   r1  past terminator on to string
;------
;*******************************************************************************


;strcpyl	ROUTINE	"r2-r3", EXPORT
;		SUB	r2, r2, #1			; max bytes to transfer
;loop$l
;		LDRB	r3, [r0], #1
;		STRB	r3, [r1], #1			; copy a byte
;		CMP	r3, #32
;		BLT	end$l				; return if it was a control character
;		SUBS	r2, r2, #1
;		BGT	loop$l				; continue if still less than max to do
;		ADD	r1, r1, #1
;end$l
;;		MOV	r3, #0
;;		STRB	r3, [r1, #-1]			; zero terminate
;		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strcpyl0
; Name
;   strcpyl0
;
; Purpose
;   This moves characters up to r2 chars
;   (r2 - 1 characters + a null) from [r0] to [r1]
;   The string is guaranteed 0 terminated even if too long
;   It updates r0 and r1 to point to after their terminating nulls
;
; Entry
;   r0  from string (not necessarily terminated)
;   r1  to string
;   r2  no. of chars to transfer including a terminator
;          (it must be 1 or greater)
;
; Exit
;   r0  past terminator on from string
;   r1  past terminator on to string
;------
;*******************************************************************************


strcpyl0	ROUTINE	"r2-r3", EXPORT

loop$l		LDRB	r3, [r0], #1
		STRB	r3, [r1], #1			; copy a byte
		CMP	r3, #32
		BLT	end$l				; return if it was a control character
		SUBS	r2, r2, #1
		BGT	loop$l				; continue if still less than max to do

end$l		MOV	r3, #0
		STRB	r3, [r1, #-1]			; zero terminate
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strdup
; Name
;   strdup
;
; Purpose
;   makes a copy of the string pointed to by r0 in the heap
;   returns an error if the memory allocation failed
;
; Entry
;   r0  string to copy
;
; Exit
;   r0  string in heap (must be freed by client)
;------
;*******************************************************************************


strdup		ROUTINE	"r1-r3", EXPORT

		MOV	r1, r0				; r1  string to copy
		BL	strlen1				; get length of string to r0
		BL	malloc				; make space for string
		EXIT	VS				; return error

		MOV	r3, r0				; to string
		MOV	r0, r1				; from string
		MOV	r1, r3				; to string
		BL	strcpy				; corrupts r0, r1

		MOV	r0, r3				; from string

		ClearV
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.memcmp
; Name
;   memcmp
;
; Purpose
;   Compares memory
;
; Entry
;   r0 = pointer to one block
;   r1 = pointer to another block
;   r2 = length to compare
;
; Exit
;   Comparison in flags
;------
;*******************************************************************************


memcmp		ROUTINE	"r0-r3", EXPORT

		CMP	r2, #0
		BLE	memcmp_end

memcmp_loop	LDRB	r3, [r0], #1
		LDRB	lr, [r1], #1
		CMP	r3, lr
		EXIT	NE
		SUBS	r2, r2, #1
		BGT	memcmp_loop

memcmp_end	SetZ
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strcmp
; Name
;   strcmp
;
; Purpose
;   Routine commented out in source code
;
; Entry
;   r0 = pointer to one string
;   r1 = pointer to another string
;
; Exit
;   Comparison in flags
;
; Notes
;   Commented out in the source code
;------
;*******************************************************************************


; strcmp		ROUTINE	"r0-r2", EXPORT
;
; strcmp_loop	LDRB	r2, [r0], #1
; 		LDRB	lr, [r1], #1
;
; 		CMP	r2, #32				; turn terminator into 0
; 		MOVLT	r2, #0
;
; 		CMP	lr, #32				; turn terminator into 0
; 		MOVLT	lr, #0
;
; 		CMP	r2, lr
; 		EXIT	NE
;
; 		CMP	r2, #0
; 		CMPEQ	lr, #0
; 		BNE	strcmp_loop			; have we reached null for both strings?
;
; 		EXIT					; return EQ


;*******************************************************************************
;----f- Director.s.Memory.strcmpi
; Name
;   strcmpi
;
; Purpose
;   Case insensitive comparison
;
; Entry
;   r0 = pointer to one string
;   r1 = pointer to another string
;
; Exit
;   Flags
;     Z            (EQ) set if strings are equal
;     ~Z && (N=V)  (GT) set if r0  string > (greater than) r1  string
;     N<>V         (LT) set if r0  string < (less than) r1  string
;------
;*******************************************************************************


strcmpi		ROUTINE	"r0-r3", EXPORT

		CMP	r0, #0
		CMPNE	r1, #0
		BEQ	error$l				; If one of them's null then exit

strcmpi_loop$l	LDRB	r2, [r0], #1
		CMP	r2, #32				; turn terminator into 0
		MOVLT	r2, #0
		UpperCase	r2, lr

		LDRB	r3, [r1], #1
		CMP	r3, #32				; turn terminator into 0
		MOVLT	r3, #0
		UpperCase	r3, lr

		CMP	r2, r3
		EXIT	NE

		CMP	r2, #0
		CMPEQ	r3, #0
		BNE	strcmpi_loop$l			; have we reached null for both strings?

		EXIT					; return EQ

error$l		ClearZ
		EXIT

;*******************************************************************************
;----f- Director.s.Memory.strcmpil
; Name
;   strcmpil
;
; Purpose
;   Case insensitive comparison up until the end of one of the strings.
;   If one string is shorter than the other, it does not matter.
;   The terminating character is a control char with an ASCII value in range of 0 to 31.
;
; Entry
;   r0 = pointer to one string
;   r1 = pointer to another string
;
; Exit
;   Flags
;     Z            (EQ) set if strings are equal
;     ~Z && (N=V)  (GT) set if r0  string > (greater than) r1  string
;     N<>V         (LT) set if r0  string < (less than) r1  string
;------
;*******************************************************************************


strcmpil	ROUTINE	"r0-r3", EXPORT

strcmpil_loop$l	LDRB	r2, [r0], #1
		CMP	r2, #32				; exit if terminator
		BLT	end$l
		UpperCase	r2, lr

		LDRB	r3, [r1], #1
		CMP	r3, #32				; exit if terminator
		BLT	end$l
		UpperCase	r3, lr

		CMP	r2, r3
		BEQ	strcmpil_loop$l
		EXIT

end$l		SetZ
		EXIT					; return EQ


;*******************************************************************************
;----f- Director.s.Memory.strcmpit
; Name
;   strcmpit
;
; Purpose
;   Case insensitive comparison using the Territory module
;   (caters for accented characters etc.)
;
;
; Entry
;   r0 = pointer to one string
;   r1 = pointer to another string
;
;
; Exit
;   Flags
;     Z            (EQ) set if strings are equal
;     ~Z && (N=V)  (GT) set if r0  string > (greater than) r1  string
;     N<>V         (LT) set if r0  string < (less than) r1  string
;
; Notes
;   If you want a reverse comparison then replace the 1st two MOVs with:
;     MOV	r2, r0
;------
;*******************************************************************************


strcmpit	ROUTINE	"r0-r3", EXPORT

		MOV	r2, r1
		MOV	r1, r0

		MOV	r0, #Territory_Current		; r0 = -1
		MOV	r3, #Territory_IgnoreCase + Territory_IgnoreAccent
		SWI	XTerritory_Collate

		CMP	r0, #0				; if the strings are equal
		EXIT


;*******************************************************************************
;----f- Director.s.Memory.strncmpi
; Name
;   strncmpi
;
; Purpose
;   Case insensitive comparison up until the end of one of the strings.
;   If one string is shorter than the other, it does not matter.
;   The terminating character is a control char in range 0 to 31.
;
; Entry
;   r0 = pointer to one string
;   r1 = pointer to another string
;   r2 = length to compare up until (and including)
;
; Exit
;   Flags
;     Z            (EQ) set if strings are equal
;     ~Z && (N=V)  (GT) set if r0  string > (greater than) r1  string
;     N<>V         (LT) set if r0  string < (less than) r1  string
;
; Notes
;   This routine is commented out, not used and is untested.
;------
;*******************************************************************************


;strncmpi	ROUTINE	"r0-r4", EXPORT
;
;strncmpi_loop$l
;		LDRB	r3, [r0], #1
;		CMP	r3, #32				; turn terminator into 0
;		MOVLT	r3, #0
;		UpperCase	r3, lr
;
;		LDRB	r4, [r1], #1
;		CMP	r4, #32				; turn terminator into 0
;		MOVLT	r4, #0
;		UpperCase	r4, lr
;
;		CMP	r3, r4
;		EXIT	NE
;
;		SUBS	r2, r2, #1
;		BEQ	end$l
;
;		CMP	r2, #0
;		CMPEQ	r3, #0
;		BNE	strncmpi_loop$l			; have we reached null for both strings?
;
;end$l		EXIT					; return EQ


;*******************************************************************************
;----f- Director.s.Memory.SWI_Memory
; Name
;   Star_Memory
;
; Purpose
;   This returns the memory used by the task
;
; Entry
;   none
;
; Exit
;   r0 = module
;   r1 = workspace
;   r2 = MallocTotalSize
;   r3 = MallocBlocks
;   r4 = dynamic area size
;   r5 = free in dynamic area
;   r6 = largest block
;------
;*******************************************************************************


Star_Memory	FOREXPORT
		ModuleVeneer
		ROUTINE	NONE

		LDR	r1, DynamicAreaNumber
		TEQ	r1, #0
		MOVEQ	r4, #0				; show no dynamic area
		BEQ	not_dynamic$l

		MOV	r0, #OSDynamicArea_Read
		SWI	XOS_DynamicArea
		MOV	r4, r2				; dynamic area size

		MOV	r0, #OSHeap_Describe
		LDR	r1, DynamicAreaBase
		SWI	XOS_Heap
		MOV	r5, r3				; free in dynamic area
		MOV	r6, r2				; largest block
not_dynamic$l

		LDR	r0, StartModule
		LDR	r0, [r0, #-4]			; module

		LDR	r1, [wp, #-4]			; workspace

		LDR	r2, MallocTotalSize

		LDR	r3, MallocBlocks

		RestoreRegs	r7
		ClearV
		MOV	pc, lr


;*******************************************************************************

		END
